import config
from Crypto.PublicKey import RSA
from Crypto.Util import randpool
import pickle
from utils import *
import hashlib
import ConfigParser
import json
from signature import *

class accountError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)
    
class Account:
    """Generic account class"""
    def __init__(self,values={}):
        self._values = values
        
    def __eq__(self,other):
        return self.getID() == other.getID()
        
    def getValue(self,valueName):
        try:
            return self._values[valueName]
        except:
            raise accountError("Value %s not present in account : %s"%(valueName,str(self.getValue)))
        
    def setValue(self,valueName,value):
        self._values[valueName] = value
        
    def getName(self):
        return self.getValue('name')
    
    def setName(self,value):
        return self.setValue('name',value)
    
    def getKey(self):
        """return a crypto.publickey.RSA object"""
        return self.getValue('key')
    
    def setKey(self,value):
        """input must be a crypto.publickey.RSA object"""
        return self.setValue('key',value)
    
    def setID(self,value):
        """set unique ID of account (string)"""
        return self.setValue('id',value)
    
    def getID(self):
         return self.getValue('id')
    
    def addNetPlug(self,netplugparam):
        """add a new netplug to account
        input must be a netplugparam object"""
        try:
            netplugs = self.getValue('netplugs')
        except:
            netplugs = net.netPlugParamsGroup([])
        netplugs.append(netplugparam)
        return self.setValue('netplugs',netplugs)
    
    def delNetPlugByName(self,name):
        """remove a netplug by his name"""
        for netplugparam in self.getNetPlugs():
            if netplugparam.getName() == name:
                self.delNetPlug(netplugparam)
                return True
        return False
    
    def delNetPlug(self,netPlugParam):
        newNetPlugs = self.getNetPlugs()
        newNetPlugs.remove(netPlugParam)
        self.setValue('netplugs',newNetPlugs)
        
    def getNetPlugs(self):
        """get all the netplug param for the account
        return a netplugparamsgroup objects"""
        try:
            ret = self.getValue('netplugs')
        except accountError:
            ret = net.netPlugParamsGroup([])
        return ret
    
    def dumps(self):
        """func that create a JSON string from contact data
        NB : this func don't export private key if exists"""
        dicForJson = {}
        for item in self._values.keys():
            #if value is string, nothing to do
            if isinstance(self._values[item],str) or isinstance(self._values[item],unicode):
                dicForJson[item] = self._values[item]
            #if value is key object, then extract state data to dump
            elif isinstance(self._values[item],RSA.RSAobj_c):
                dicRsa = {}
                dicRsa["state"] = self._values[item].publickey().__getstate__()
                dicRsa["type"] = "RSA.RSAobj_c"
                dicForJson[item] = dicRsa
            #handle netplugparamsgroup
            elif isinstance(self._values[item],net.netPlugParamsGroup):
                dic = {}
                dic["state"] = self._values[item].dumps()
                dic["type"] = "net.netPlugParamsGroup"
                dicForJson[item] = dic                 
            else:
                raise accountError("Item %s in account %s has an unknown type"%(item,self.getID()))
        return json.dumps(dicForJson)
    
    def loads(self,jsonString):
        """func that import contact data from JSON string"""
        dicJson = json.loads(jsonString)
        for item in dicJson.keys():
            #check type
            #if string, no conversion needed
            if isinstance(dicJson[item],str) or isinstance(dicJson[item],unicode):
                self.setValue(item,dicJson[item])
            elif isinstance(dicJson[item],dict):
                #else we need to determine the type of the object
                if dicJson[item].has_key("type"):
                    #RSA key object data type
                    if dicJson[item]["type"] == "RSA.RSAobj_c":
                        key = RSA.RSAobj_c(None)
                        #convert to long for RSAObject
                        for i,v in dicJson[item]["state"].iteritems():
                            dicJson[item]["state"][i] = long(v)
                        key.__setstate__(dicJson[item]["state"])
                        self.setValue(item,key)
                    #netplugparamsgroup object data type
                    elif dicJson[item]["type"] == "net.netPlugParamsGroup":
                        netplugparams = net.netPlugParamsGroup()
                        netplugparams.loads(dicJson[item]["state"])
                        self.setValue(item,netplugparams)
                    else:
                        warningOutput("unknown type %s to import"%(dicJson[item]["type"],))
                else:
                    raise accountError("Import account error. Import data corrupt.")
            else:
                raise accountError("Import account error. Import data corrupt.")
    
        
#FIXME : dirty import
import information
import friends
import net
        
class myAccount(Account):
    """actual account in use"""
    ACCOUNT_SETTINGS_SECTION = 'account'
    def __init__(self):
        Account.__init__(self,{})
        keys = config.getSetting(self.ACCOUNT_SETTINGS_SECTION,'key')
        if keys:
            self._values['key'] = pickle.loads(keys)
        self._values['name'] = config.getSetting(self.ACCOUNT_SETTINGS_SECTION,'name')
        self._values['id'] = config.getSetting(self.ACCOUNT_SETTINGS_SECTION,'id')
        netplugs = config.getSetting(self.ACCOUNT_SETTINGS_SECTION,'netplugs')
        if netplugs:
            self._values['netplugs'] = pickle.loads(netplugs)

            
##    def clearSettings(self):
##        """Clear myAccount settings"""
##        config.clearSectionSetting(self.ACCOUNT_SETTINGS_SECTION)
##        warningOutput('myAccount : Account data cleared')
            
    def setValue(self,valueName,value):
        Account.setValue(self,valueName,value)
        #convert to string
        if type(value) != type(str()):
            value = pickle.dumps(value)
        config.setSetting(self.ACCOUNT_SETTINGS_SECTION,valueName,value)
        #update personnal info in database if private key is present (needed for info signature)
        if self.getKey():
            information.setPersonalInformation(information.INFO_ID_CONTACT,friends.FRIENDSGROUP_ALL,self.dumps())
        
    def generateNewKey(self):
        """generate a new key for personal account
        !!!! should be used once !!!!"""
        #TODO evaluer consequences si cle deja presente
        pool = randpool.RandomPool()
        RSAKey = RSA.generate(2048,pool.get_bytes)
        self.setKey(RSAKey)
        warningOutput('myAccount : New Key Generated')
        
    def signData(self,data):
        """ sign a raw data string with local account key 
        return a signature object """
        pool = randpool.RandomPool()
        digest = hashlib.sha1(data).digest()
        sign = self.getKey().sign(digest,pool.get_bytes)
        return signature(SIGNATURE_TYPE_RSA,sign)
        
    def signInformation(self,info):
        """ sign an Information object with local account key
        return a signature object """
        infoOutput("SIGN information (%s)"%info.getSerializeNoSign())
        return self.signData(info.getSerializeNoSign())
    
my = None

def getMyAccount():
    global my
    if not my:
        my = myAccount()
    return my

def createNewMyAccount(name):
    """create a new account for the user
    ATTENTION : erase previous account"""
    global my
    my = myAccount()
    my.setName(name)
    pool = randpool.RandomPool()
    my.setID(hashlib.sha1(pool.get_bytes(1024)).hexdigest())
    my.generateNewKey()
    warningOutput("NEW MyAccount created for %s !"%name)
    
